<template>
    <div id="sockAll">
        <ul id="messages" ref="chatMessagesContainer">
            <li v-for="item in messageList" :key="item" :class="username === item.username ? 'mainer' : 'other'">
                <div class="avatar">
                    <template v-if="username === item.username">
                        <img src="../../../assets/images/userImage.webp" alt="" />
                    </template>
                    <template v-else>
                        <img src="../../../assets/images/userImage.webp" alt="" />
                    </template>
                </div>
                <div class="words">
                    <span v-if="!item.isImage && !item.isAudio">
                        {{ item.message }}
                    </span>
                    <img v-if="item.isImage" :src="item.message" alt="" />
                    <audio v-if="item.isAudio" :src="item.message" controls style="width: 270px; height: 50px;"></audio>
                </div>
                <!-- 如果消息发送失败，显示红色感叹号 -->
                <div style="margin-right: 5px;margin-top: 10px;" @click="resendMessage(item)">
                    <template v-if="item.sendFailed">
                        <span class="failed-icon">&#10071;</span>
                    </template>
                </div>

            </li>
        </ul>
        <div id="saying" :style="{ opacity: isSpeaking ? 1 : 0 }">
            <img src="../../../assets/images/yuyin.gif" alt="" />
        </div>
        <div id="nav_bottom">
            <form id="form" @submit.prevent="sendOnEnter">
                <input id="input" autocomplete="off" v-model="dispatcher" @keyup.enter="sendOnEnter"
                    @keydown.enter.prevent />
                <button @click.prevent="openEmoji">
                    <img src="../../../assets/images/emoji.png" alt="" />
                </button>
                <template v-if="dispatcher.length > 0">
                    <button @click.prevent="send">发送</button>
                </template>
                <template v-else>
                    <button @click.prevent="openPopup">
                        <img src="../../../assets/images/plus.png" alt="" />
                    </button>
                </template>

            </form>
            <div id="popup" :style="{ display: flag }">
                <van-grid :border="false" :gutter="5">
                    <van-grid-item icon="photo-o" text="图片" @click="selectImage" />
                    <van-grid-item icon="volume-o" text="转文字" @touchstart="toggleSpeechRecognition" @touchend="btnup" />
                    <van-grid-item icon="photo-o" text="开始录音" @click="recStart" />
                    <van-grid-item icon="photo-o" text="结束录音" @click="recStop" />
                </van-grid>
            </div>
            <div id="emoji" :style="{ display: flag1 }">
                <div v-for="row in emojilist" :key="row" style="display: flex; justify-content: space-between;">
                    <div v-for="emoji in row" :key="emoji" @click="dispatcher += emoji">
                        {{ emoji }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { io } from 'socket.io-client';
import axios from 'axios';
import { ref, defineProps, onMounted, watch, nextTick ,onUnmounted} from 'vue';
// 语音转文字
// 测试语音识别
// const prompt = ref('')
const isSpeaking = ref(false);
let recognition = null; // 语音识别对象
let finalResult = ''; // 保存最终结果的变量
let interimResult = ''; // 保存中间结果的变量
const toggleSpeechRecognition = () => {
    if (!recognition) {
        recognition = new webkitSpeechRecognition();
        recognition.continuous = true;
        recognition.interimResults = true;
        recognition.lang = 'zh-CN';
        recognition.onresult = event => {
            interimResult = ''; // 清空中间结果
            for (let i = event.resultIndex; i < event.results.length; i++) {
                if (event.results[i].isFinal) {
                    finalResult += event.results[i][0].transcript;
                } else {
                    interimResult += event.results[i][0].transcript;
                }
            }
            // prompt.value = finalResult + interimResult;
            dispatcher.value = finalResult + interimResult;
        };
        recognition.start();
        isSpeaking.value = true;
        console.log('语音识别已启动');
    }
};
const btnup = () => {
    if (recognition) {
        recognition.stop();
        isSpeaking.value = false;
        recognition = null;
        finalResult = '';
        interimResult = '';
        dispatcher.value = '';
    }
};

// emoji 表情
const emojilist = ref([
    ['😀', '😁', '😂', '🤣', '😃', '😅', '😆'],
    ['😉', '😊', '😋', '😎', '😍', '😘', '😗'],
    ['😙', '😚', '🙂', '🤗', '🤔', '😐', '😑'],
    ['😶', '🙄', '😏', '😣', '😥', '😮', '🤐'],
    ['😯', '😪', '😫', '😴', '😌', '😛', '😜'],
    ['😝', '🤤', '😒', '😓', '😔', '😕', '🙃'],
    ['🤑', '😲', '🙁', '😖', '😟', '😤', '😢'],
    ['😭', '😧', '😨', '😩', '😬', '😰', '😳'],
    ['😱', '😵', '😡', '😠', '😷', '🤒', '🤕'],
]);

const flag = ref('none');
const flag1 = ref('none');

// 展开底部导航
const openPopup = () => {
    if (flag.value === 'none') {
        flag.value = 'block';
        flag1.value = 'none';
    } else {
        flag.value = 'none';
        flag1.value = 'none';
    }
};
// 展开表情导航
const openEmoji = () => {
    if (flag1.value === 'none') {
        flag1.value = 'block';
        flag.value = 'none';
    } else {
        flag1.value = 'none';
        flag.value = 'none';
    }
};

const sendOnEnter = (event) => {
    if (event.key === 'Enter' && !event.shiftKey) {
        send();
        event.preventDefault();
    }
};

const props = defineProps({
    username: String,
});

const messageList = ref([]);
const dispatcher = ref('');

const socket = io('ws://127.0.0.1:3001', {
    autoConnect: false, // 手动控制连接
    reconnection: true, // 开启重连功能
    reconnectionAttempts: Infinity, // 无限次重连尝试
    reconnectionDelay: 1000, // 重连间隔时间（毫秒）
    reconnectionDelayMax: 5000, // 最大重连间隔时间（毫秒）
});

onMounted(() => {
    socket.connect();

    // 监听连接断开事件
    socket.on('disconnect', (reason) => {
        console.log('Disconnected:', reason);
    });

    // 监听重新连接事件
    socket.on('reconnect', (attemptNumber) => {
        console.log('Reconnected after', attemptNumber, 'attempts');
    });
});
const heartbeatInterval = ref(null);

onMounted(() => {
    // 连接成功后开始发送心跳包
    socket.on('connect', () => {
        heartbeatInterval.value = setInterval(() => {
            socket.emit('heartbeat', 'ping');
        }, 5000); // 每 5 秒发送一次心跳包
    });

    // 接收服务器的心跳响应
    socket.on('heartbeat', (data) => {
        console.log('Received heartbeat response:', data);
    });
});

onUnmounted(() => {
    // 组件销毁时清除心跳定时器
    if (heartbeatInterval.value) {
        clearInterval(heartbeatInterval.value);
    }
});

socket.on('connection', (socket) => {
    console.log('a user connected');
    socket.on('disconnect', () => {
        console.log('user disconnected');
    });
});



socket.on('imageMessage', (data) => {
    console.log('Received image message: ', data);
    const imgBaseUrl = "http://localhost:3001/uploads";
    const imageName = data.path.split("\\").pop();
    const imageUrl = imgBaseUrl + '/' + imageName;
    // url.value = imageUrl
    console.log('最终路径', imageUrl);
    // 根据实际情况判断发送者
    let sender = data.username ? data.username : props.username;
    let userobj = {
        username: sender,
        message: imageUrl,
        isImage: data.isUploaded || false,
        sendFailed: false, // 默认发送成功
    };
    messageList.value.push(userobj);
    console.log(messageList.value);
});
// 接收音频消息
socket.on('audioMessage', (data) => {
    const audioBaseUrl = "http://localhost:3001/audioUploads";
    const audioName = data.path.split("\\").pop();
    const audioUrl = audioBaseUrl + '/' + audioName;
    console.log('Received audio URL:', audioUrl);
    let sender = data.username ? data.username : props.username;
    let userobj = {
        username: sender,
        message: audioUrl,
        isAudio: true,
        sendFailed: false
    };
    messageList.value.push(userobj);
    const audioElement = new Audio(audioUrl);
    audioElement.addEventListener('error', () => {
        console.error('Failed to play audio:', audioElement.error);
        // 可以在这里添加提示用户的代码，如弹出对话框等
        alert('Failed to play audio');
    });
});

// 检测网络连接状态
const onNetworkChange = () => {
    if (navigator.onLine) {
        // 网络连接恢复，可以进行一些操作，比如重新连接 socket
    } else {
        // 网络断开，进行相应处理
    }
};

onMounted(() => {
    window.addEventListener('online', onNetworkChange);
    window.addEventListener('offline', onNetworkChange);
    recOpen()
});


const messageIdCounter = ref(0); // 消息编号计数器
const pendingMessages = ref<{ id: number; message: any }[]>([]); // 待确认的消息
const send = () => {
    const newMessage = {
        username: props.username,
        message: dispatcher.value,
        sendFailed: false,
        id: messageIdCounter.value++
    };
    if (dispatcher.value && navigator.onLine) {
        socket.emit('message', newMessage, props.username);
        pendingMessages.value.push(newMessage);
        dispatcher.value = '';
        flag.value = 'none';
        flag1.value = 'none';
    } else if (!navigator.onLine) {
        newMessage.sendFailed = true;
        messageList.value.push(newMessage);
        dispatcher.value = '';
    }
};

// 监听服务器的确认消息
socket.on('messageAck', (ackId) => {
    const index = pendingMessages.value.findIndex((msg) => msg.id === ackId);
    if (index !== -1) {
        const confirmedMessage = pendingMessages.value.splice(index, 1)[0];
        // 检查消息是否已经在消息列表中
        const existingIndex = messageList.value.findIndex((msg) => msg.id === ackId);
        if (existingIndex === -1) {
            messageList.value.push(confirmedMessage);
        }
    }
});

// 处理服务器广播的消息，只处理其他用户的消息
socket.on('message', (msg, user) => {
    if (user !== props.username) {
        let userobj = {
            username: user,
            message: msg,
            sendFailed: false
        };
        if (navigator.onLine) {
            messageList.value.push(userobj);
        } else {
            userobj.sendFailed = true;
            messageList.value.push(userobj);
        }
    }
});

// 重新发送单个消息的方法
const resendMessage = (failedMessage) => {
    if (navigator.onLine) {
        socket.emit('message', failedMessage.message, props.username);
        // 更新消息状态为发送中
        failedMessage.sendFailed = false;
        // 从消息列表中移除原始的发送失败的消息
        const index = messageList.value.indexOf(failedMessage);
        if (index !== -1) {
            messageList.value.splice(index, 1);
        }
    } else {
        console.log('网络已断开，无法重新发送消息。');
    }
};

// 重新发送所有失败消息的方法（可在网络恢复时调用）
const resendFailedMessages = () => {
    const failedMessages = messageList.value.filter(item => item.sendFailed);
    failedMessages.forEach((failedMessage) => {
        socket.emit('message', failedMessage.message, props.username);
        // 更新消息状态为发送中
        failedMessage.sendFailed = false;
    });
};

const selectImage = () => {
    if (navigator.onLine) {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = 'image/*';
        input.onchange = async (event) => {
            const file = event.target.files[0];
            if (file) {
                const formData = new FormData();
                formData.append('image', file);
                formData.append('username', props.username);
                try {
                    await axios.post('http://127.0.0.1:3001/upload', formData, {
                        headers: {
                            'Content-Type': 'multipart/form-data',
                        },
                    });
                } catch (error) {
                    console.error('上传图片失败：', error);
                }
            }
        };
        input.click();
    } else {
        console.log('网络已断开，无法上传图片。');
    }
};

//设置消息滚动到底部
const chatMessagesContainer = ref(null);

//将滑轮滚动到底部
const scrollToBottom = () => {
    nextTick(() => {
        if (chatMessagesContainer.value) {
            chatMessagesContainer.value.scrollTop =
                chatMessagesContainer.value.scrollHeight + 10;
        }
    });
};
// 打开页面是滚动到最底部
onMounted(() => {
    scrollToBottom();
});

//监听消息列表变化watch(
watch(
    () => messageList.value,
    () => {
        scrollToBottom();
    }
)

// 必须引入的核心
import Recorder from 'recorder-core';
// 引入 mp3 格式支持文件；如果需要多个格式支持，把这些格式的编码引擎 js 文件放到后面统统引入进来即可
import 'recorder-core/src/engine/mp3';
import 'recorder-core/src/engine/mp3-engine';
// 录制 wav 格式的用这一句就行
import 'recorder-core/src/engine/wav';
// 可选的插件支持项，这个是波形可视化插件
import 'recorder-core/src/extensions/waveview';

let rec:any ;
let recBlob: any;
let wave: any;
const recwave = ref(null);

function recOpen(callback: () => void) {
    // 创建录音对象
    rec = Recorder({
        type: 'wav',
        sampleRate: 16000,
        bitRate: 16,
        onProcess: (
            buffers: any,
            powerLevel: any,
            bufferDuration: any,
            bufferSampleRate: any,
            newBufferIdx: any,
            asyncEnd: any,
        ) => {
            // 录音实时回调，大约 1 秒调用 12 次本回调
            // 可实时绘制波形，实时上传（发送）数据
            if (wave) {
                wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
            }
        },
    });
    if (!rec) {
        alert('当前浏览器不支持录音功能！');
        return;
    }
    // 打开录音，获得权限
    rec.open(
        () => {
            console.log('录音已打开');
            if (recwave.value) {
                // 创建音频可视化图形绘制对象
                wave = Recorder.WaveView({ elem: recwave.value });
            }
            if (callback) {
                callback(); // 调用回调函数
            }
        },
        (msg: any, isUserNotAllow: any) => {
            // 用户拒绝了录音权限，或者浏览器不支持录音
            console.log((isUserNotAllow ? 'UserNotAllow，' : '') + '无法录音:' + msg);
        },
    );
}

function recStart() {
    if (!rec) {
        recOpen(() => {
            if (rec) {
                rec.start();
                console.log('已开始录音');
            } else {
                console.error('无法打开录音');
            }
        });
    } else {
        rec.start();
        console.log('已开始录音');
    }
}
function recStop() {
    if (!rec) {
        console.error('未打开录音');
        return;
    }

    rec.stop(
        (blob, duration) => {
            const formData = new FormData();
            formData.append('audio', blob);
            formData.append('username', props.username);
            axios.post('http://127.0.0.1:3001/uploadAudio', formData, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            }).then(response => {
                console.log('音频上传成功', response);
            }).catch(error => {
                console.error('音频上传失败', error);
            });
            rec.close();
            rec = null;
        },
        (err) => {
            console.error('结束录音出错：' + err);
            rec.close();
            rec = null;
        }
    );
}


function playAudio() {
    const audio = new Audio();
    if (!audio.canPlayType('audio/wav')) {
        console.error('Browser does not support.wav format');
        // 可以考虑显示提示信息给用户
        alert('不支持')
    }
    audio.src = URL.createObjectURL(recBlob);
    audio.play();
}
</script>

<style lang="scss" scoped>
#sockAll {
    margin: 0;
    padding-bottom: 3rem;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    height: 100%;
    width: 100%;
}

#saying {
    width: 90%;
    height: 150px;
    background: pink;
    position: fixed;
    bottom: 250px;
    left: 22px;
    z-index: 10000;

    img {
        width: 100%;
        height: 100%;
    }
}

#nav_bottom {
    width: 100%;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    border-top: 1px solid #ccc;
    align-items: center;
    justify-content: center;
    background: white;

    #form {
        width: 100%;
        background: rgb(238, 236, 236);
        padding: 0.25rem;
        display: flex;
        height: 3rem;
        box-sizing: border-box;
        backdrop-filter: blur(10px);
    }

    #input {
        border: none;
        padding: 0 1rem;
        flex-grow: 1;
        border-radius: 2rem;
        margin: 0.25rem;
    }

    #input:focus {
        outline: none;
    }

    #form>button {
        background: #27c669;
        border: none;
        padding: 0 1rem;
        margin: 0.25rem;
        border-radius: 3px;
        outline: none;
        color: #fff;

        img {
            width: 25px;
            height: 25px;
            margin-top: 3px;
        }
    }

    #popup {
        width: 100%;
        height: 80px;
        padding-top: 6px;
    }

    #emoji {
        width: 100%;
        height: 200px;
    }
}

#messages {
    list-style-type: none;
    margin: 0;
    padding: 0;

    li {
        display: flex;
    }

    .failed-icon {
        font-size: 25px;
        color: red;
    }


}

.mainer {
    width: 100%;
    min-height: 50px;
    text-align: right;
    flex-direction: row-reverse;
    margin-bottom: 20px;

    .avatar {
        width: 40px;
        height: 40px;

        img {
            width: 100%;
            height: 100%;
        }

    }

    .words {
        max-width: 70%;
        height: auto;
        background: #95ec69;
        text-align: left;
        margin-right: 10px;
        border-radius: 10px;
        line-height: 2em;
        padding: 10px;

        img {
            width: 140px;
            height: 100px;
        }
    }
}

.other {
    width: 100%;
    height: auto;
    margin-bottom: 20px;

    .avatar {
        width: 40px;
        height: 40px;

        img {
            width: 100%;
            height: 100%;
        }
    }

    .words {
        max-width: 60%;
        height: auto;
        background: #ffffff;
        text-align: left;
        margin-left: 10px;
        border-radius: 10px;
        line-height: 2em;
        padding: 10px;

        img {
            width: 140px;
            height: 100px;
        }
    }

    .failed-icon {
        color: red;
    }
}

.audio-message {
    margin-bottom: 10px;
}
</style>

<!-- 1 -->